package callbacks

import (
	"context"
	"time"
)

// CallbackEvent represents an event that triggers callbacks
type CallbackEvent string

const (
	EventStart    CallbackEvent = "start"
	EventEnd      CallbackEvent = "end"
	EventError    CallbackEvent = "error"
	EventProgress CallbackEvent = "progress"
	EventLog      CallbackEvent = "log"
	EventMetric   CallbackEvent = "metric"
)

// CallbackData contains data passed to callbacks
type CallbackData struct {
	Event     CallbackEvent          // type of event
	Operation string                 // operation being performed
	Message   string                 // event message
	Error     error                  // error if any
	Progress  float32                // progress percentage (0-100)
	Metrics   map[string]interface{} // metrics data
	Timestamp time.Time              // when the event occurred
	Duration  time.Duration          // duration since operation start
	Context   map[string]interface{} // additional context
}

// Callback defines the interface for callback handlers
type Callback interface {
	// HandleEvent handles a callback event
	HandleEvent(ctx context.Context, data *CallbackData) error

	// ShouldHandle returns whether this callback should handle the event
	ShouldHandle(event CallbackEvent) bool
}

// CallbackManager manages callbacks
type CallbackManager struct {
	callbacks []Callback
}

// NewCallbackManager creates a new callback manager
func NewCallbackManager() *CallbackManager {
	return &CallbackManager{
		callbacks: make([]Callback, 0),
	}
}

// RegisterCallback registers a new callback
func (m *CallbackManager) RegisterCallback(callback Callback) {
	m.callbacks = append(m.callbacks, callback)
}

// HandleEvent handles an event by calling all registered callbacks
func (m *CallbackManager) HandleEvent(ctx context.Context, data *CallbackData) error {
	for _, callback := range m.callbacks {
		if callback.ShouldHandle(data.Event) {
			if err := callback.HandleEvent(ctx, data); err != nil {
				return err
			}
		}
	}
	return nil
}

// LoggingCallback implements logging callback
type LoggingCallback struct {
	logger Logger
}

// Logger defines the interface for logging
type Logger interface {
	Info(msg string, fields map[string]interface{})
	Error(msg string, err error, fields map[string]interface{})
	Debug(msg string, fields map[string]interface{})
}

// NewLoggingCallback creates a new logging callback
func NewLoggingCallback(logger Logger) *LoggingCallback {
	return &LoggingCallback{logger: logger}
}

func (c *LoggingCallback) HandleEvent(ctx context.Context, data *CallbackData) error {
	fields := map[string]interface{}{
		"operation": data.Operation,
		"timestamp": data.Timestamp,
		"duration":  data.Duration.String(),
	}

	if len(data.Metrics) > 0 {
		fields["metrics"] = data.Metrics
	}

	if len(data.Context) > 0 {
		fields["context"] = data.Context
	}

	switch data.Event {
	case EventError:
		c.logger.Error(data.Message, data.Error, fields)
	case EventLog:
		c.logger.Info(data.Message, fields)
	default:
		c.logger.Debug(data.Message, fields)
	}

	return nil
}

func (c *LoggingCallback) ShouldHandle(event CallbackEvent) bool {
	return true
}

// MetricsCallback implements metrics collection callback
type MetricsCallback struct {
	collector MetricsCollector
}

// MetricsCollector defines the interface for collecting metrics
type MetricsCollector interface {
	RecordMetric(name string, value interface{}, tags map[string]string)
	RecordDuration(name string, duration time.Duration, tags map[string]string)
}

// NewMetricsCallback creates a new metrics callback
func NewMetricsCallback(collector MetricsCollector) *MetricsCallback {
	return &MetricsCallback{collector: collector}
}

func (c *MetricsCallback) HandleEvent(ctx context.Context, data *CallbackData) error {
	tags := map[string]string{
		"operation": data.Operation,
		"event":     string(data.Event),
	}

	// Record duration for completed operations
	if data.Event == EventEnd {
		c.collector.RecordDuration(data.Operation, data.Duration, tags)
	}

	// Record custom metrics
	for name, value := range data.Metrics {
		c.collector.RecordMetric(name, value, tags)
	}

	return nil
}

func (c *MetricsCallback) ShouldHandle(event CallbackEvent) bool {
	return event == EventEnd || event == EventMetric
}

// ProgressCallback implements progress reporting callback
type ProgressCallback struct {
	reporter ProgressReporter
}

// ProgressReporter defines the interface for reporting progress
type ProgressReporter interface {
	UpdateProgress(operation string, progress float32, message string)
}

// NewProgressCallback creates a new progress callback
func NewProgressCallback(reporter ProgressReporter) *ProgressCallback {
	return &ProgressCallback{reporter: reporter}
}

func (c *ProgressCallback) HandleEvent(ctx context.Context, data *CallbackData) error {
	if data.Event == EventProgress {
		c.reporter.UpdateProgress(data.Operation, data.Progress, data.Message)
	}
	return nil
}

func (c *ProgressCallback) ShouldHandle(event CallbackEvent) bool {
	return event == EventProgress
}
